Better names for taxa
library(tidyverse)
pull_lowlev <- function(taxaVec){
taxaVec <- dplyr::select(Kingdom:Genus)
taxaVec <- na.omit(taxaVec)
taxaVec[length(taxaVec)]
}
theTaxa <- ps %>% tax_table() %>% as("matrix") %>% data.frame() #%>% as.data.frame()
theTaxa[] <- lapply(theTaxa, as.character)
theTaxa$ASVNum <- rownames(theTaxa) %>% parse_number
theTaxa$ASVName <- rownames(theTaxa)
myLowist <- theTaxa %>% pivot_longer(Kingdom:Genus) %>% na.omit %>% group_by(ASVName) %>% summarise(Lowist = last(value))
theTaxa <- theTaxa %>% left_join(myLowist, by = "ASVName")
theTaxa <- theTaxa %>% mutate(JName = str_c(Lowist, ASVNum, sep = "_"))
theTaxa <- theTaxa %>% column_to_rownames("ASVName")
head(theTaxa)
tt2 <- tax_table(theTaxa)
Coercing from data.frame class to character matrix
prior to building taxonomyTable.
This could introduce artifacts.
Check your taxonomyTable, or coerce to matrix manually.
rownames(tt2) <- rownames(theTaxa)
colnames(tt2) <- colnames(theTaxa)
ps_retax <- ps
tax_table(ps_retax) <- tt2
Remove blanks
ps_noblank <- subset_samples(ps_retax, Strain != "Blank")
ps_plusone <- transform_sample_counts(ps_noblank, function(x) x + 1)
Convert to relative abundance
psra <- ps %>% transform_sample_counts( function(x) x/sum(x))
Normalize microibal counts to oyster counts
ps_oyster <- ps %>% subset_taxa(Order == "Ostreoida")
ps_not_oyster <- ps %>% subset_taxa(Order != "Ostreoida")
oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
not_oyster_counts <- otu_table(ps_not_oyster)@.Data
over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
over_oyster_log10 <- log10(over_oyster)
This is a variance normalizing tranformaiton. Its apparently an alternative to rarifying the data.
deseq_pre <- phyloseq_to_deseq2(ps, design = ~ Project)
# deseq_counts <- estimateSizeFactors(deseq_pre, type = "poscounts")
# deseq_counts_vst <- varianceStabilizingTransformation(deseq_counts)
# vst_trans_count_tab <- assay(deseq_counts_vst)
# Ella on Slack
#deseq_pre
dds = deseq_pre[rowSums(counts(deseq_pre)) > 5,]
dds_esf <- estimateSizeFactors(dds, type = "poscounts")
dds01 <- DESeq(dds_esf)
dds_res <- results(dds01)
dds_counts <- counts(dds01, normalized = TRUE)
ps_dds <- ps_retax
otu_table(ps_dds) <- otu_table(dds_counts, taxa_are_rows = TRUE)
ets filter just the rows
ps_common <- filter_taxa(ps_dds, function(x) sum(x > 2) > (0.2*length(x)), TRUE)
ps_common
phyloseq-class experiment-level object
otu_table() OTU Table: [ 463 taxa and 35 samples ]
sample_data() Sample Data: [ 35 samples by 6 sample variables ]
tax_table() Taxonomy Table: [ 463 taxa by 9 taxonomic ranks ]
Interestingly, this step doesn’t seem to effect the ordinations much.
ps_oyster <- ps_dds %>% subset_taxa(Order == "Ostreoida")
ps_not_oyster <- ps_common %>% subset_taxa(Order != "Ostreoida") # ok if not common, so just using dds
oyster_sums <- otu_table(ps_oyster)@.Data %>% apply(MARGIN = 2, sum)
not_oyster_counts <- otu_table(ps_not_oyster)@.Data
over_oyster <- sweep(not_oyster_counts, 2, oyster_sums, "/")
# so this suddently contains zeros, making everything fail, but it didn't used to. What gives?
over_oyster_log10 <- log10(over_oyster)
(detection_thresh <- min(na.omit(over_oyster[over_oyster > 0])))
[1] 7.091444e-06
over_oyster_log10 <- log10(over_oyster + detection_thresh)
ps_oo <- ps_not_oyster
otu_table(ps_oo) <- otu_table(over_oyster, taxa_are_rows = TRUE)
ps_oo <- subset_samples(ps_oo, (SampleID %in% c(names(oyster_sums[oyster_sums > 0])))) # remove cases
ps_oo_log <- ps_not_oyster
otu_table(ps_oo_log) <- otu_table(over_oyster_log10, taxa_are_rows = TRUE)
ps_oo_log_ss <- subset_samples(ps_oo_log, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))
ps_oo_ss <- subset_samples(ps_oo, !(Project =="Mock"& Strain == "Even" & Run == "NoCrash"))
Make the one figure for the paper
A function I use
converts phyloseq sample data to a data frame
samd_to_df <- function(samd){
df <- samd %>% sample_data %>% .@.Data %>% lapply(as.character) %>% data.frame
colnames(df) <- samd@names
rownames(df) <- samd@row.names
df
}
Actually run the RDA
ps_oo_log_ss1 <- ps_oo_log_ss %>% subset_samples(((Run =="NoCrash" & Project == "NoCrash") | (Run == "Crash4" & Project == "Crash")) & Treatment %in% c("Fed", "Starve", "Pre"))
test_rda <- rda(t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"), data = samd_to_df(sample_data(ps_oo_log_ss1)))
test_rda
Call: rda(formula = t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"),
data = samd_to_df(sample_data(ps_oo_log_ss1)))
Inertia Proportion Rank
Total 398.5716 1.0000
Constrained 219.1114 0.5497 3
Unconstrained 179.4602 0.4503 24
Inertia is variance
Eigenvalues for constrained axes:
RDA1 RDA2 RDA3
168.88 41.18 9.06
Eigenvalues for unconstrained axes:
PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8
45.32 28.06 17.27 13.72 8.04 6.94 5.75 5.37
(Showing 8 of 24 unconstrained eigenvalues)
test_rda_anova <- anova(test_rda, by = "margin", permutations = how(nperm = 99999))
test_rda_anova
Permutation test for rda under reduced model
Marginal effects of terms
Permutation: free
Number of permutations: 99999
Model: rda(formula = t(otu_table(ps_oo_log_ss1)) ~ Project + as.factor(Strain == "Wild") + as.factor(Treatment == "Fed"), data = samd_to_df(sample_data(ps_oo_log_ss1)))
Df Variance F Pr(>F)
Project 1 154.338 20.6403 1e-05 ***
as.factor(Strain == "Wild") 1 18.132 2.4248 0.05165 .
as.factor(Treatment == "Fed") 1 38.107 5.0962 0.00232 **
Residual 24 179.460
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
myScores <- scores(test_rda, choices = c(1:4), scaling = "symmetric")
Sample Data
mySamples <- left_join(
myScores$sites %>% data.frame %>% rownames_to_column("Sample"),
ps_oo_log_ss1 %>% sample_data() %>% samd_to_df %>% rownames_to_column("Sample"),
by = "Sample"
)
Percent variance explained
eigsum <- sum(c(test_rda$CCA$eig, test_rda$CA$eig))
cca_eig <- test_rda$CCA$eig / eigsum
ca_eig <- test_rda$CA$eig / eigsum
all_eig <- c(cca_eig, ca_eig)
all_eig["RDA1"]
RDA1
0.4237047
rda_eig_pct <- data.frame(all_eig) %>% rownames_to_column("Ax") %>% dplyr::rename(EigPct = "all_eig") %>%
mutate(Axis2 = ordered(Ax, levels = Ax))
rda_eig_pct
Species
Select which species we will show, targeting ones that are far from 0, 0.
mySpecies <- left_join(
myScores$species %>% data.frame %>% rownames_to_column("ASV"),
ps_oo_log_ss1 %>% tax_table() %>% .@.Data %>% as.data.frame %>% rownames_to_column("ASV")
) %>% mutate(RDADist = sqrt(RDA1^2 + RDA2^2 + RDA3 ^2)) %>%
arrange(-RDADist) %>%
head(10) %>%
mutate(Rank = 1:10)
Joining, by = "ASV"
mySpecies
mySpecies %>% ggplot(aes(x = RDA1, y = RDA2, label = JName)) + geom_point() + ggrepel::geom_text_repel()

Calculate Centroids
myCent <- myScores$centroids %>% data.frame %>% rownames_to_column("Treatment") %>%
tidyr::extract(Treatment, c("Type", "Condition"), "([A-Z][a-z]+)([A-Z].*)", remove = FALSE) %>%
mutate(Type = if_else(str_detect(Treatment, "Wild"), "Strain", Type)) %>%
mutate(Condition = if_else(Type == "Strain" & str_detect(Treatment, "FALSE"), "Tame", Condition)) %>%
mutate(Condition = if_else(Type == "Strain" & str_detect(Treatment, "TRUE"), "Wild", Condition)) %>%
mutate(Type = if_else(str_detect(Treatment, "Fed"), "Feeding", Type)) %>%
mutate(Condition = if_else(Type == "Feeding" & str_detect(Treatment, "FALSE"), "StarvedOrPre", Condition)) %>%
mutate(Condition = if_else(Type == "Feeding" & str_detect(Treatment, "TRUE"), "Fed", Condition))
myCent2 <- myCent %>% filter((Condition %in% c("Fed", "Wild", "Crash")))
Main Figure
ccaPlot_1V2_A <- mySamples %>% ggplot(aes(x = RDA1, y = RDA2)) +
geom_point(size = 3, stroke = 3, aes(shape = Project, color = Strain == "Wild", fill = Treatment), alpha = 1) +
scale_shape_manual(values = c(21:25)) + scale_color_manual(values = c("gray40", "black")) + scale_fill_manual(values = c(Fed = "Blue", Pre = "DarkGreen", Starve = "Orange", Crash = "Pink", NoCrash = "White", Wild = "White"))+
geom_point(data = mySpecies, size = 4, shape = "+") +
ggrepel::geom_text_repel(data = mySpecies, aes(label = JName) , size = 3) +
guides(fill = guide_legend(override.aes = list(shape = 21)), color = guide_legend(override.aes = list(shape = 21))) +
theme(legend.position = "bottom")
ccaLegend <- get_legend(ccaPlot_1V2_A)
ccaPlot_1V2_B <- ccaPlot_1V2_A +
geom_label(data = myCent2, aes(label = Condition, x = RDA1 * 1.75, y = RDA2 * 2, fill = Condition) , size = 5) +
geom_segment(data = myCent2, aes(x = 0, y = 0, xend = RDA1 * 2.5, yend = RDA2 * 2.5), arrow = arrow(length = unit(0.1, "in")), alpha = 0.5, size = 1) +
coord_fixed(sqrt(test_rda$CCA$eig[2]/test_rda$CCA$eig[1])) +
labs(x = paste0("RDA1", " (", scales::percent(all_eig["RDA1"]), ")"),
y = paste0("RDA2", " (", scales::percent(all_eig["RDA2"]), ")")
) +
cowplot::theme_cowplot() + theme(legend.position = "none")
#plot_grid(ccaPlot_1V2_B, ccaLegend)
ProtoNewFig <- plot_grid(ccaPlot_1V2_B, ccaLegend, nrow = 2, rel_heights = c(10,1))
ggsave("ProtoNewFig.svg", ProtoNewFig, width = 8, height = 6)
View Main Figure
ProtoNewFig

I’m not sure why “NoCrash” and “Wild” showed up in the legend. It didn’t used to do that, but I’m not going to bother to correct this right now.
Seeing which species relate to crash vs non-crash
We don’t show these figures in the paper, but we do refer to them.
Initial data wrangling
ps_oo_log_ss2 <- ps_oo_log_ss1
sample_data(ps_oo_log_ss2) <- mySamples %>% column_to_rownames("Sample") %>% sample_data()
Reshaping to long takes a little while. (~ 20 seconds)
melt_oo_log_ss2 <- psmelt(ps_oo_log_ss2)
melt2_oo_log_ss2 <- melt_oo_log_ss2 %>%
mutate(logAbundance = Abundance) %>%
mutate(Abundance = 10^(Abundance))
melt2_oo_log_ss2 <- melt2_oo_log_ss2 %>% left_join(mySpecies %>% select(RDA1.Spec = RDA1, JName), by = "JName")
melt2_oo_log_ss2 %>% head
Stuff
Run an lme to see if each microbe is related to project, holding out treatment as a mixed effect.
modframe <- melt2_oo_log_ss2 %>% select(Project:Group, logAbundance, Kingdom:Genus, JName ) %>% group_by(JName) %>% nest(data = Project:logAbundance) %>%
#mutate(mod = map(data, ~tidy(lm(data = ., logAbundance ~ Project)))) %>%
mutate(lme = map(data, ~tidy(lmer(data = ., logAbundance ~ Project + (1|Treatment) + (1|Strain)))))
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
boundary (singular) fit: see ?isSingular
modframe01 <- modframe %>% unnest(lme) %>% filter(term == "ProjectNoCrash") %>% select(Kingdom:JName, estimate, std.error, p.value) %>% mutate(fdr = p.adjust(p.value, method = "BH"))
ggplotly(
ggplot(modframe01, aes(x = estimate, y = log10(p.value), color = Kingdom, JName = JName)) + geom_point() +
scale_color_manual(values = c(Bacteria = "Gray10", Eukaryota = "blue", Archaea = "red")) +
geom_hline(aes(yintercept = log10(0.01)))
)
Everything below the line is statistically significant. Mouse over the dots to see which bacteria are which. If plotly is giving you problems, comment out the ggplotly bits and this shows up as a normal plot.
How many significant and non significant ASVs are there?
modframe01 %>% ungroup %>% summarise(signif = sum(p.value < 0.01), total = length(p.value)) %>% mutate(frachits = signif/total)
63 % of the asvs are related to treatment p < 0.01.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNvdXJjZSgiTWFrZVBoeWxvc2VxT2JqZWN0LlIiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHBoeWxvc2VxKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoY293cGxvdCkKIGxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKIGxpYnJhcnkoYnJvb20ubWl4ZWQpCmxpYnJhcnkocGxvdGx5KQoKYGBgCgojIEJldHRlciBuYW1lcyBmb3IgdGF4YQoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgpgYGB7cn0KcHVsbF9sb3dsZXYgPC0gZnVuY3Rpb24odGF4YVZlYyl7CiAgdGF4YVZlYyA8LSBkcGx5cjo6c2VsZWN0KEtpbmdkb206R2VudXMpCiAgdGF4YVZlYyA8LSBuYS5vbWl0KHRheGFWZWMpCiAgdGF4YVZlY1tsZW5ndGgodGF4YVZlYyldCn0KCnRoZVRheGEgPC0gcHMgJT4lIHRheF90YWJsZSgpICU+JSBhcygibWF0cml4IikgJT4lIGRhdGEuZnJhbWUoKSAgIyU+JSBhcy5kYXRhLmZyYW1lKCkKdGhlVGF4YVtdIDwtIGxhcHBseSh0aGVUYXhhLCBhcy5jaGFyYWN0ZXIpCnRoZVRheGEkQVNWTnVtIDwtIHJvd25hbWVzKHRoZVRheGEpICU+JSBwYXJzZV9udW1iZXIKdGhlVGF4YSRBU1ZOYW1lIDwtIHJvd25hbWVzKHRoZVRheGEpCgoKbXlMb3dpc3QgPC0gdGhlVGF4YSAlPiUgcGl2b3RfbG9uZ2VyKEtpbmdkb206R2VudXMpICU+JSBuYS5vbWl0ICU+JSBncm91cF9ieShBU1ZOYW1lKSAlPiUgc3VtbWFyaXNlKExvd2lzdCA9IGxhc3QodmFsdWUpKQoKdGhlVGF4YSA8LSB0aGVUYXhhICU+JSBsZWZ0X2pvaW4obXlMb3dpc3QsIGJ5ID0gIkFTVk5hbWUiKQp0aGVUYXhhIDwtIHRoZVRheGEgJT4lIG11dGF0ZShKTmFtZSA9IHN0cl9jKExvd2lzdCwgQVNWTnVtLCBzZXAgPSAiXyIpKQp0aGVUYXhhIDwtIHRoZVRheGEgJT4lIGNvbHVtbl90b19yb3duYW1lcygiQVNWTmFtZSIpCmhlYWQodGhlVGF4YSkKCgp0dDIgPC0gdGF4X3RhYmxlKHRoZVRheGEpCnJvd25hbWVzKHR0MikgPC0gcm93bmFtZXModGhlVGF4YSkKY29sbmFtZXModHQyKSA8LSBjb2xuYW1lcyh0aGVUYXhhKQpwc19yZXRheCA8LSBwcwp0YXhfdGFibGUocHNfcmV0YXgpIDwtIHR0MgpgYGAKClJlbW92ZSBibGFua3MKYGBge3J9CnBzX25vYmxhbmsgPC0gc3Vic2V0X3NhbXBsZXMocHNfcmV0YXgsIFN0cmFpbiAhPSAiQmxhbmsiKQpwc19wbHVzb25lIDwtIHRyYW5zZm9ybV9zYW1wbGVfY291bnRzKHBzX25vYmxhbmssIGZ1bmN0aW9uKHgpIHggKyAxKQpgYGAKCkNvbnZlcnQgdG8gcmVsYXRpdmUgYWJ1bmRhbmNlCmBgYHtyfQpwc3JhIDwtIHBzICU+JSB0cmFuc2Zvcm1fc2FtcGxlX2NvdW50cyggZnVuY3Rpb24oeCkgeC9zdW0oeCkpCmBgYAoKTm9ybWFsaXplIG1pY3JvaWJhbCBjb3VudHMgdG8gb3lzdGVyIGNvdW50cwpgYGB7cn0KcHNfb3lzdGVyIDwtIHBzICU+JSBzdWJzZXRfdGF4YShPcmRlciA9PSAiT3N0cmVvaWRhIikKcHNfbm90X295c3RlciA8LSBwcyAlPiUgc3Vic2V0X3RheGEoT3JkZXIgIT0gIk9zdHJlb2lkYSIpCm95c3Rlcl9zdW1zIDwtIG90dV90YWJsZShwc19veXN0ZXIpQC5EYXRhICU+JSBhcHBseShNQVJHSU4gPSAyLCBzdW0pCm5vdF9veXN0ZXJfY291bnRzIDwtIG90dV90YWJsZShwc19ub3Rfb3lzdGVyKUAuRGF0YQoKb3Zlcl9veXN0ZXIgPC0gc3dlZXAobm90X295c3Rlcl9jb3VudHMsIDIsIG95c3Rlcl9zdW1zLCAiLyIpCm92ZXJfb3lzdGVyX2xvZzEwIDwtIGxvZzEwKG92ZXJfb3lzdGVyKQpgYGAKClRoaXMgaXMgYSB2YXJpYW5jZSBub3JtYWxpemluZyB0cmFuZm9ybWFpdG9uLiBJdHMgYXBwYXJlbnRseSBhbiBhbHRlcm5hdGl2ZSB0byByYXJpZnlpbmcgdGhlIGRhdGEuIApgYGB7cn0KZGVzZXFfcHJlIDwtIHBoeWxvc2VxX3RvX2Rlc2VxMihwcywgZGVzaWduID0gfiBQcm9qZWN0KQojIGRlc2VxX2NvdW50cyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRlc2VxX3ByZSwgdHlwZSA9ICJwb3Njb3VudHMiKQojIGRlc2VxX2NvdW50c192c3QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRlc2VxX2NvdW50cykKIyB2c3RfdHJhbnNfY291bnRfdGFiIDwtIGFzc2F5KGRlc2VxX2NvdW50c192c3QpCgojIEVsbGEgb24gU2xhY2sKI2Rlc2VxX3ByZQpkZHMgPSBkZXNlcV9wcmVbcm93U3Vtcyhjb3VudHMoZGVzZXFfcHJlKSkgPiA1LF0KZGRzX2VzZiA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKGRkcywgdHlwZSA9ICJwb3Njb3VudHMiKQpkZHMwMSA8LSBERVNlcShkZHNfZXNmKQpkZHNfcmVzIDwtIHJlc3VsdHMoZGRzMDEpCgpkZHNfY291bnRzIDwtIGNvdW50cyhkZHMwMSwgbm9ybWFsaXplZCA9IFRSVUUpCgpwc19kZHMgPC0gcHNfcmV0YXgKb3R1X3RhYmxlKHBzX2RkcykgPC0gb3R1X3RhYmxlKGRkc19jb3VudHMsIHRheGFfYXJlX3Jvd3MgPSBUUlVFKQpgYGAKCmV0cyBmaWx0ZXIganVzdCB0aGUgcm93cwoKYGBge3J9CnBzX2NvbW1vbiA8LSBmaWx0ZXJfdGF4YShwc19kZHMsIGZ1bmN0aW9uKHgpIHN1bSh4ID4gMikgPiAoMC4yKmxlbmd0aCh4KSksIFRSVUUpCnBzX2NvbW1vbgpgYGAKCkludGVyZXN0aW5nbHksIHRoaXMgc3RlcCBkb2Vzbid0IHNlZW0gdG8gZWZmZWN0IHRoZSBvcmRpbmF0aW9ucyBtdWNoLgoKYGBge3J9CnBzX295c3RlciA8LSBwc19kZHMgJT4lIHN1YnNldF90YXhhKE9yZGVyID09ICJPc3RyZW9pZGEiKQpwc19ub3Rfb3lzdGVyIDwtIHBzX2NvbW1vbiAlPiUgc3Vic2V0X3RheGEoT3JkZXIgIT0gIk9zdHJlb2lkYSIpICMgb2sgaWYgbm90IGNvbW1vbiwgc28ganVzdCB1c2luZyBkZHMKb3lzdGVyX3N1bXMgPC0gb3R1X3RhYmxlKHBzX295c3RlcilALkRhdGEgJT4lIGFwcGx5KE1BUkdJTiA9IDIsIHN1bSkKbm90X295c3Rlcl9jb3VudHMgPC0gb3R1X3RhYmxlKHBzX25vdF9veXN0ZXIpQC5EYXRhCgpvdmVyX295c3RlciA8LSBzd2VlcChub3Rfb3lzdGVyX2NvdW50cywgMiwgb3lzdGVyX3N1bXMsICIvIikKIyBzbyB0aGlzIHN1ZGRlbnRseSBjb250YWlucyB6ZXJvcywgbWFraW5nIGV2ZXJ5dGhpbmcgZmFpbCwgYnV0IGl0IGRpZG4ndCB1c2VkIHRvLiBXaGF0IGdpdmVzPwpvdmVyX295c3Rlcl9sb2cxMCA8LSBsb2cxMChvdmVyX295c3RlcikKKGRldGVjdGlvbl90aHJlc2ggPC0gbWluKG5hLm9taXQob3Zlcl9veXN0ZXJbb3Zlcl9veXN0ZXIgPiAwXSkpKQpvdmVyX295c3Rlcl9sb2cxMCA8LSBsb2cxMChvdmVyX295c3RlciArIGRldGVjdGlvbl90aHJlc2gpCgpwc19vbyA8LSBwc19ub3Rfb3lzdGVyCm90dV90YWJsZShwc19vbykgPC0gb3R1X3RhYmxlKG92ZXJfb3lzdGVyLCB0YXhhX2FyZV9yb3dzID0gVFJVRSkKcHNfb28gPC0gc3Vic2V0X3NhbXBsZXMocHNfb28sIChTYW1wbGVJRCAlaW4lIGMobmFtZXMob3lzdGVyX3N1bXNbb3lzdGVyX3N1bXMgPiAwXSkpKSkgIyByZW1vdmUgY2FzZXMKCnBzX29vX2xvZyA8LSBwc19ub3Rfb3lzdGVyCm90dV90YWJsZShwc19vb19sb2cpIDwtIG90dV90YWJsZShvdmVyX295c3Rlcl9sb2cxMCwgdGF4YV9hcmVfcm93cyA9IFRSVUUpCgpwc19vb19sb2dfc3MgPC0gc3Vic2V0X3NhbXBsZXMocHNfb29fbG9nLCAhKFByb2plY3QgPT0iTW9jayImIFN0cmFpbiA9PSAiRXZlbiIgJiBSdW4gPT0gIk5vQ3Jhc2giKSkKcHNfb29fc3MgPC0gc3Vic2V0X3NhbXBsZXMocHNfb28sICEoUHJvamVjdCA9PSJNb2NrIiYgU3RyYWluID09ICJFdmVuIiAmIFJ1biA9PSAiTm9DcmFzaCIpKQpgYGAKCiMgTWFrZSB0aGUgb25lIGZpZ3VyZSBmb3IgdGhlIHBhcGVyCgojIyBBIGZ1bmN0aW9uIEkgdXNlCmNvbnZlcnRzIHBoeWxvc2VxIHNhbXBsZSBkYXRhIHRvIGEgZGF0YSBmcmFtZQpgYGB7cn0Kc2FtZF90b19kZiA8LSBmdW5jdGlvbihzYW1kKXsKICBkZiA8LSBzYW1kICU+JSBzYW1wbGVfZGF0YSAlPiUgLkAuRGF0YSAlPiUgbGFwcGx5KGFzLmNoYXJhY3RlcikgJT4lIGRhdGEuZnJhbWUKICBjb2xuYW1lcyhkZikgPC0gc2FtZEBuYW1lcwogIHJvd25hbWVzKGRmKSA8LSBzYW1kQHJvdy5uYW1lcwogIGRmCn0KYGBgCgojIyBBY3R1YWxseSBydW4gdGhlIFJEQQpgYGB7cn0KCnBzX29vX2xvZ19zczEgPC0gcHNfb29fbG9nX3NzICU+JSBzdWJzZXRfc2FtcGxlcygoKFJ1biA9PSJOb0NyYXNoIiAmIFByb2plY3QgPT0gIk5vQ3Jhc2giKSB8IChSdW4gPT0gIkNyYXNoNCIgJiBQcm9qZWN0ID09ICJDcmFzaCIpKSAmIFRyZWF0bWVudCAlaW4lIGMoIkZlZCIsICJTdGFydmUiLCAiUHJlIikpCgp0ZXN0X3JkYSA8LSByZGEodChvdHVfdGFibGUocHNfb29fbG9nX3NzMSkpIH4gUHJvamVjdCArIGFzLmZhY3RvcihTdHJhaW4gPT0gIldpbGQiKSArIGFzLmZhY3RvcihUcmVhdG1lbnQgPT0gIkZlZCIpLCBkYXRhID0gc2FtZF90b19kZihzYW1wbGVfZGF0YShwc19vb19sb2dfc3MxKSkpCnRlc3RfcmRhCnRlc3RfcmRhX2Fub3ZhIDwtIGFub3ZhKHRlc3RfcmRhLCBieSA9ICJtYXJnaW4iLCBwZXJtdXRhdGlvbnMgPSBob3cobnBlcm0gPSA5OTk5OSkpCnRlc3RfcmRhX2Fub3ZhCm15U2NvcmVzIDwtIHNjb3Jlcyh0ZXN0X3JkYSwgY2hvaWNlcyA9IGMoMTo0KSwgc2NhbGluZyA9ICJzeW1tZXRyaWMiKQpgYGAKIyMgU2FtcGxlIERhdGEKYGBge3J9Cm15U2FtcGxlcyA8LSBsZWZ0X2pvaW4oCm15U2NvcmVzJHNpdGVzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZSIpLApwc19vb19sb2dfc3MxICU+JSBzYW1wbGVfZGF0YSgpICU+JSBzYW1kX3RvX2RmICU+JSByb3duYW1lc190b19jb2x1bW4oIlNhbXBsZSIpLApieSA9ICJTYW1wbGUiCikKYGBgCgojIyBQZXJjZW50IHZhcmlhbmNlIGV4cGxhaW5lZApgYGB7cn0KZWlnc3VtIDwtIHN1bShjKHRlc3RfcmRhJENDQSRlaWcsIHRlc3RfcmRhJENBJGVpZykpCmNjYV9laWcgPC0gdGVzdF9yZGEkQ0NBJGVpZyAvIGVpZ3N1bQpjYV9laWcgPC0gdGVzdF9yZGEkQ0EkZWlnIC8gZWlnc3VtCmFsbF9laWcgPC0gYyhjY2FfZWlnLCBjYV9laWcpCmFsbF9laWdbIlJEQTEiXSAKYGBgCgpgYGB7cn0KcmRhX2VpZ19wY3QgPC0gZGF0YS5mcmFtZShhbGxfZWlnKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJBeCIpICU+JSBkcGx5cjo6cmVuYW1lKEVpZ1BjdCA9ICJhbGxfZWlnIikgJT4lCiAgbXV0YXRlKEF4aXMyID0gb3JkZXJlZChBeCwgbGV2ZWxzID0gQXgpKQpyZGFfZWlnX3BjdApgYGAKCiMjIFNwZWNpZXMKU2VsZWN0IHdoaWNoIHNwZWNpZXMgd2Ugd2lsbCBzaG93LCB0YXJnZXRpbmcgb25lcyB0aGF0IGFyZSBmYXIgZnJvbSAwLCAwLgpgYGB7cn0KbXlTcGVjaWVzIDwtIGxlZnRfam9pbigKICBteVNjb3JlcyRzcGVjaWVzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIkFTViIpLApwc19vb19sb2dfc3MxICU+JSB0YXhfdGFibGUoKSAlPiUgLkAuRGF0YSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJBU1YiKQopICU+JSBtdXRhdGUoUkRBRGlzdCA9IHNxcnQoUkRBMV4yICsgUkRBMl4yICsgUkRBMyBeMikpICU+JSAKICBhcnJhbmdlKC1SREFEaXN0KSAlPiUKICBoZWFkKDEwKSAlPiUKICBtdXRhdGUoUmFuayA9IDE6MTApCm15U3BlY2llcwpgYGAKCiMjIENhbGN1bGF0ZSBDZW50cm9pZHMKCmBgYHtyfQpteUNlbnQgPC0gbXlTY29yZXMkY2VudHJvaWRzICU+JSBkYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIlRyZWF0bWVudCIpICU+JQogIHRpZHlyOjpleHRyYWN0KFRyZWF0bWVudCwgYygiVHlwZSIsICJDb25kaXRpb24iKSwgIihbQS1aXVthLXpdKykoW0EtWl0uKikiLCAgcmVtb3ZlID0gRkFMU0UpICU+JQogIG11dGF0ZShUeXBlID0gaWZfZWxzZShzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIldpbGQiKSwgIlN0cmFpbiIsIFR5cGUpKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJTdHJhaW4iICYgc3RyX2RldGVjdChUcmVhdG1lbnQsICJGQUxTRSIpLCAiVGFtZSIsIENvbmRpdGlvbikpICU+JQogIG11dGF0ZShDb25kaXRpb24gPSBpZl9lbHNlKFR5cGUgPT0gIlN0cmFpbiIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIlRSVUUiKSwgIldpbGQiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoVHlwZSA9IGlmX2Vsc2Uoc3RyX2RldGVjdChUcmVhdG1lbnQsICJGZWQiKSwgIkZlZWRpbmciLCBUeXBlKSkgJT4lCiAgbXV0YXRlKENvbmRpdGlvbiA9IGlmX2Vsc2UoVHlwZSA9PSAiRmVlZGluZyIgJiBzdHJfZGV0ZWN0KFRyZWF0bWVudCwgIkZBTFNFIiksICJTdGFydmVkT3JQcmUiLCBDb25kaXRpb24pKSAlPiUKICBtdXRhdGUoQ29uZGl0aW9uID0gaWZfZWxzZShUeXBlID09ICJGZWVkaW5nIiAmIHN0cl9kZXRlY3QoVHJlYXRtZW50LCAiVFJVRSIpLCAiRmVkIiwgQ29uZGl0aW9uKSkKCm15Q2VudDIgPC0gbXlDZW50ICU+JSBmaWx0ZXIoKENvbmRpdGlvbiAlaW4lIGMoIkZlZCIsICJXaWxkIiwgIkNyYXNoIikpKQpgYGAKCiMjIyBNYWluIEZpZ3VyZQpgYGB7cn0KY2NhUGxvdF8xVjJfQSA8LSBteVNhbXBsZXMgJT4lIGdncGxvdChhZXMoeCA9IFJEQTEsIHkgPSBSREEyKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHN0cm9rZSA9IDMsIGFlcyhzaGFwZSA9IFByb2plY3QsIGNvbG9yID0gU3RyYWluID09ICJXaWxkIiwgZmlsbCA9IFRyZWF0bWVudCksIGFscGhhID0gMSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIxOjI1KSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JheTQwIiwgImJsYWNrIikpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyhGZWQgPSAiQmx1ZSIsIFByZSA9ICJEYXJrR3JlZW4iLCBTdGFydmUgPSAiT3JhbmdlIiwgQ3Jhc2ggPSAiUGluayIsIE5vQ3Jhc2ggPSAiV2hpdGUiLCBXaWxkID0gIldoaXRlIikpKwogIGdlb21fcG9pbnQoZGF0YSA9IG15U3BlY2llcywgc2l6ZSA9IDQsIHNoYXBlID0gIisiKSArIAogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhID0gbXlTcGVjaWVzLCBhZXMobGFiZWwgPSBKTmFtZSkgLCBzaXplID0gMykgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSAyMSkpLCBjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjEpKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKY2NhTGVnZW5kIDwtIGdldF9sZWdlbmQoY2NhUGxvdF8xVjJfQSkKCmNjYVBsb3RfMVYyX0IgPC0gY2NhUGxvdF8xVjJfQSArCiAgZ2VvbV9sYWJlbChkYXRhID0gbXlDZW50MiwgYWVzKGxhYmVsID0gQ29uZGl0aW9uLCB4ID0gUkRBMSAqIDEuNzUsIHkgPSBSREEyICogMiwgZmlsbCA9IENvbmRpdGlvbikgLCBzaXplID0gNSkgKwogIGdlb21fc2VnbWVudChkYXRhID0gbXlDZW50MiwgYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IFJEQTEgKiAyLjUsIHllbmQgPSBSREEyICogMi41KSwgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMSwgImluIikpLCBhbHBoYSA9IDAuNSwgc2l6ZSA9IDEpICsKICBjb29yZF9maXhlZChzcXJ0KHRlc3RfcmRhJENDQSRlaWdbMl0vdGVzdF9yZGEkQ0NBJGVpZ1sxXSkpICsKICBsYWJzKHggPSBwYXN0ZTAoIlJEQTEiLCAiICgiLCBzY2FsZXM6OnBlcmNlbnQoYWxsX2VpZ1siUkRBMSJdKSwgIikiKSwKICAgICAgIHkgPSBwYXN0ZTAoIlJEQTIiLCAiICgiLCBzY2FsZXM6OnBlcmNlbnQoYWxsX2VpZ1siUkRBMiJdKSwgIikiKQogICAgICAgKSArCiAgY293cGxvdDo6dGhlbWVfY293cGxvdCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKI3Bsb3RfZ3JpZChjY2FQbG90XzFWMl9CLCBjY2FMZWdlbmQpClByb3RvTmV3RmlnIDwtIHBsb3RfZ3JpZChjY2FQbG90XzFWMl9CLCBjY2FMZWdlbmQsIG5yb3cgPSAyLCByZWxfaGVpZ2h0cyA9IGMoMTAsMSkpCmdnc2F2ZSgiUHJvdG9OZXdGaWcuc3ZnIiwgUHJvdG9OZXdGaWcsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKYGBgCgojIyMgVmlldyBNYWluIEZpZ3VyZQpgYGB7cn0KUHJvdG9OZXdGaWcKYGBgCgpJJ20gbm90IHN1cmUgd2h5ICJOb0NyYXNoIiBhbmQgIldpbGQiIHNob3dlZCB1cCBpbiB0aGUgbGVnZW5kLiBJdCBkaWRuJ3QgdXNlZCB0byBkbyB0aGF0LCBidXQgSSdtIG5vdCBnb2luZyB0byBib3RoZXIgdG8gY29ycmVjdCB0aGlzIHJpZ2h0IG5vdy4KCiMgU2VlaW5nIHdoaWNoIHNwZWNpZXMgcmVsYXRlIHRvIGNyYXNoIHZzIG5vbi1jcmFzaApXZSBkb24ndCBzaG93IHRoZXNlIGZpZ3VyZXMgaW4gdGhlIHBhcGVyLCBidXQgd2UgZG8gcmVmZXIgdG8gdGhlbS4KCiMjIEluaXRpYWwgZGF0YSB3cmFuZ2xpbmcKCmBgYHtyfQpwc19vb19sb2dfc3MyIDwtIHBzX29vX2xvZ19zczEKc2FtcGxlX2RhdGEocHNfb29fbG9nX3NzMikgPC0gIG15U2FtcGxlcyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJTYW1wbGUiKSAlPiUgc2FtcGxlX2RhdGEoKQpgYGAKClJlc2hhcGluZyB0byBsb25nIHRha2VzIGEgbGl0dGxlIHdoaWxlLiAofiAyMCBzZWNvbmRzKQpgYGB7cn0KbWVsdF9vb19sb2dfc3MyIDwtIHBzbWVsdChwc19vb19sb2dfc3MyKQpgYGAKCmBgYHtyfQptZWx0Ml9vb19sb2dfc3MyIDwtIG1lbHRfb29fbG9nX3NzMiAlPiUKICBtdXRhdGUobG9nQWJ1bmRhbmNlID0gQWJ1bmRhbmNlKSAlPiUKICBtdXRhdGUoQWJ1bmRhbmNlID0gMTBeKEFidW5kYW5jZSkpCm1lbHQyX29vX2xvZ19zczIgPC0gbWVsdDJfb29fbG9nX3NzMiAlPiUgbGVmdF9qb2luKG15U3BlY2llcyAlPiUgc2VsZWN0KFJEQTEuU3BlYyA9IFJEQTEsIEpOYW1lKSwgYnkgPSAiSk5hbWUiKQptZWx0Ml9vb19sb2dfc3MyICU+JSBoZWFkCmBgYAoKIyMgU3R1ZmYKClJ1biBhbiBsbWUgdG8gc2VlIGlmIGVhY2ggbWljcm9iZSBpcyByZWxhdGVkIHRvIHByb2plY3QsIGhvbGRpbmcgb3V0IHRyZWF0bWVudCBhcyBhIG1peGVkIGVmZmVjdC4KCmBgYHtyfQptb2RmcmFtZSA8LSBtZWx0Ml9vb19sb2dfc3MyICU+JSBzZWxlY3QoUHJvamVjdDpHcm91cCwgbG9nQWJ1bmRhbmNlLCBLaW5nZG9tOkdlbnVzLCBKTmFtZSApICU+JSBncm91cF9ieShKTmFtZSkgJT4lIG5lc3QoZGF0YSA9IFByb2plY3Q6bG9nQWJ1bmRhbmNlKSAlPiUKICAjbXV0YXRlKG1vZCA9IG1hcChkYXRhLCB+dGlkeShsbShkYXRhID0gLiwgbG9nQWJ1bmRhbmNlIH4gUHJvamVjdCkpKSkgJT4lCiAgbXV0YXRlKGxtZSA9IG1hcChkYXRhLCB+dGlkeShsbWVyKGRhdGEgPSAuLCBsb2dBYnVuZGFuY2UgfiBQcm9qZWN0ICsgKDF8VHJlYXRtZW50KSArICgxfFN0cmFpbikpKSkpCmBgYAoKYGBge3J9Cm1vZGZyYW1lMDEgPC0gbW9kZnJhbWUgJT4lIHVubmVzdChsbWUpICU+JSBmaWx0ZXIodGVybSA9PSAiUHJvamVjdE5vQ3Jhc2giKSAlPiUgc2VsZWN0KEtpbmdkb206Sk5hbWUsIGVzdGltYXRlLCBzdGQuZXJyb3IsIHAudmFsdWUpICU+JSBtdXRhdGUoZmRyID0gcC5hZGp1c3QocC52YWx1ZSwgbWV0aG9kID0gIkJIIikpCmBgYAoKYGBge3J9CmdncGxvdGx5KApnZ3Bsb3QobW9kZnJhbWUwMSwgYWVzKHggPSBlc3RpbWF0ZSwgeSA9IGxvZzEwKHAudmFsdWUpLCAgY29sb3IgPSBLaW5nZG9tLCBKTmFtZSA9IEpOYW1lKSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhCYWN0ZXJpYSA9ICJHcmF5MTAiLCBFdWthcnlvdGEgPSAiYmx1ZSIsIEFyY2hhZWEgPSAicmVkIikpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbG9nMTAoMC4wMSkpKQopCmBgYAoKRXZlcnl0aGluZyBiZWxvdyB0aGUgbGluZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBNb3VzZSBvdmVyIHRoZSBkb3RzIHRvIHNlZSB3aGljaCBiYWN0ZXJpYSBhcmUgd2hpY2guIElmIHBsb3RseSBpcyBnaXZpbmcgeW91IHByb2JsZW1zLCBjb21tZW50IG91dCB0aGUgYGdncGxvdGx5YCBiaXRzIGFuZCB0aGlzIHNob3dzIHVwIGFzIGEgbm9ybWFsIHBsb3QuCgpIb3cgbWFueSBzaWduaWZpY2FudCBhbmQgbm9uIHNpZ25pZmljYW50IEFTVnMgYXJlIHRoZXJlPwoKYGBge3J9Cm1vZGZyYW1lMDEgJT4lIHVuZ3JvdXAgJT4lIHN1bW1hcmlzZShzaWduaWYgPSBzdW0ocC52YWx1ZSA8IDAuMDEpLCB0b3RhbCA9IGxlbmd0aChwLnZhbHVlKSkgJT4lIG11dGF0ZShmcmFjaGl0cyA9IHNpZ25pZi90b3RhbCkgCmBgYAo2MyAlIG9mIHRoZSBhc3ZzIGFyZSByZWxhdGVkIHRvIHRyZWF0bWVudCBwIDwgMC4wMS4=